/**
 * @author Da Mu
 * @version 0.1.0
 * @mail 317782199@qq.com
 */
import * as Cesium from 'cesium';
// import kriging from 'kriging/src/kriging';
import proxy from '@/config/host';
import CesiumNavigation from 'cesium-navigation-es6';
import AMapLoader from '@amap/amap-jsapi-loader';
// import colorbrewer from 'colorbrewer';
import { Scene3D, THREE, ExtendedObject3D } from "@enable3d/phaser-extension";
import { SceneVueInteractiveSubscription } from '@/utils/SceneVueInteractiveSubscription';
import gsap from 'gsap';

import { socket } from '@/utils/Socket';
import { PerformanceLog, Add, Center } from '@/utils/decorator';
import axios from 'axios';
import log from '@/utils/logs';
import VConsole from 'vconsole';

const vConsole = new VConsole();

let _enCan;
let _cescan;
let _playerCoordinatesA = {};

let puid = null;

/**
 * @class
 * -http service config
 */
class Http {
    constructor() {
        /**
         * @this {amap_key}
         * get your amap key
         */
        this.amap_key = proxy.development.AMAP_KEY;
        /**
         * @this {amap_secret}
         * get your amap secret
         */
        this.amap_secret = proxy.development.AMAP_SECRET;
        /**
         * @this {td_token}
         * get your MAPWORLD tokne
         */
        this.td_token = '';
        /**
         * @this {ces_token}
         * get your cesiun tokne
         */
        this.ces_token = '';

        /**
         * @description api
         */
        this.http = `${proxy.development.API}`;
        this.static_http = `${proxy.development.STATIC_API}`;
        this.path_img = `${proxy.development.PATH_IMG}`;
        this.path_json = `${proxy.development.PATH_JSON}`;
        this.path_models = `${proxy.development.PATH_MODELS}`;
    };
};

/**
 * @class
 * -socket init
 */
class Socket {
    constructor() {
        /**@description Events */
        socket.on('connect', () => {
            // log.debug('Connected to server');
            console.log('Connected to server');
        });
        socket.on('disconnect', () => {
            // log.debug('user disconnected');
            console.log('user disconnected');
        });
    };

    utils(obj1, obj2, tolerance = 1e-10) {

        function floatsEqual(a, b, tol = tolerance) {
            return Math.abs(a - b) < tol;
        };

        return floatsEqual(obj1.startLon, obj2.startLon) &&
            floatsEqual(obj1.startLat, obj2.startLat);
    };

    getPlayerUID() {
        SceneVueInteractiveSubscription.publish('get_wv_puid');
    };

    readyStartGame() {
        SceneVueInteractiveSubscription.subscribe('res_wv_puid', wv_puid => {
            puid = wv_puid;
            socket.emit('IO_ROOM_PLAYERS_READY_START_GAME', wv_puid);
            socket.emit('IO_ROOM_UI_STATUS_PLAYER_VALUE', wv_puid);
        });
    };

    listenStartGame() {
        /**@description Events */
        socket.on('IO_ROOM_PLAYERS_START_GAME_DATA', uuid => {
            // if (!puid === uuid) return;

            if (!puid === uuid) return;
            this.closeRotateGlobe();
            SceneVueInteractiveSubscription.publish("open_ui_status");

        });
    };

    listenTimerPlayersInfo(that) {
        /**@description Events */
        socket.on('IO_ROOM_PLAYERS_INFO_TIMER_QUEUE_DATA', obj => {
            // if (!puid === uuid) return;

            if (!puid === obj.playerUID) return;

            const gcj02Point = new that._AMap.LngLat(obj.playerLong, obj.playerLat);

            const _playerCoordinatesB = {
                startLon: gcj02Point.lng,
                startLat: gcj02Point.lat
            };
            const data = this.utils(_playerCoordinatesA, _playerCoordinatesB);

            if (!data) {
                console.log('timerPlayerInof:', _playerCoordinatesA, _playerCoordinatesB);

                that.PlayerGltfMovePathAnima([{
                    startLon: _playerCoordinatesA.startLon,
                    startLat: _playerCoordinatesA.startLat,
                    startH: 0,
                    stopLon: _playerCoordinatesB.startLon,
                    stopLat: _playerCoordinatesB.startLat,
                    stopH: 0,
                    uid: puid
                }], 5);
                _playerCoordinatesA = {
                    startLon: _playerCoordinatesB.startLon,
                    startLat: _playerCoordinatesB.startLat,
                };
            };
            socket.off('IO_ROOM_PLAYERS_INFO_TIMER_QUEUE_DATA');

        });
    };

    listenBuildingHeartbeatDetection() {
        /**@description Events */
        socket.on('IO_ROOM_UI_STATUS_BUILDING_CRON_JOB', obj => {
            if (obj.playerUID === puid) {
                SceneVueInteractiveSubscription.publish("ui_build_timer", obj.totalMilliseconds);
                if (obj.playerBuild === 1) {
                    console.log('安全屋建造完成');
                    this.GameCheckSafeHouseIsBuilding();
                };
            };
        });
    };

    joinGameWorld() {
        socket.emit('IO_ROOM_JOIN_GAME_WORLD', 'JOIN');
    };

    getUiAirCronJob() {
        /**@description Events */
        socket.on('IO_ROOM_UI_STATUS_AIR_CRON_JOB', num => {
            SceneVueInteractiveSubscription.publish("ui_status_air", num);
        });
    };

    getUiStatusValue() {
        /**@description Events */
        socket.on('IO_ROOM_UI_STATUS_PLAYER_VALUE_DATA', obj => {
            SceneVueInteractiveSubscription.publish("ui_status_player", obj);
        });
    };

    on() {
        socket.connect();
    };

    off() {
        socket.disconnect();
    };
};

/**
 * @class
 * -anima gsap
 */
const _anima = Symbol('_anima');
class CreateGsap {
    static [_anima](sp) {
        gsap.to(sp[0], {
            opacity: sp[2],
            duration: 1,
            ease: 'power1.Out'
        });
        gsap.to(sp[1], {
            opacity: sp[3],
            duration: 1,
            ease: 'power1.Out'
        });
        gsap.to(sp[1], {
            opacity: sp[4],
            duration: 1,
            ease: 'power1.Out',
            delay: .5
        });
    };

    static init(sp) {
        if (!CreateGsap.instance) {
            CreateGsap.instance = new CreateGsap();
            CreateGsap[_anima](sp);
        };
        return CreateGsap.instance;
    };
};

/**
 * @class
 * -listen vue events
 */
class ListenVueEventsController {
    Events() {
        /**@description Event */
        SceneVueInteractiveSubscription.subscribe('OpenAirdrop', () => {
            this.GameAirdropModelMovePathAnima(15, -3);
        });

        /**@description Event */
        SceneVueInteractiveSubscription.subscribe('open_building_timer', async () => {
            await this.GameSafeHouseBuildingTimer();
        });

        /**@description Event */
        SceneVueInteractiveSubscription.subscribe('enable3d_switch_scene', str => {
            socket.emit('IO_ROOM_ENABLE3D_SWITCH_SCENE', {
                id: puid,
                sw: str
            });
        });
    };
};

/**
 * @class
 * cesium main
 */
@Add
class CesiumMain extends Http {

    _houseGltfCoordinateMap = new Map();

    _StaticModelGltfCoordinateDataMap = new Map();
    _StaticModelGltfCoordinateModelMap = new Map();

    _StaticModelGltfPartialCoordinateModelMap = new Map();

    _OpenAirdropBoxAnima = true;

    constructor(container, realLight, openClock, openTerrain, openMAPWORLD, openAMAP, krigingCan, cesCan, enableCan) {
        super();

        this._ui_camera_status = {
            air: 0,
            box: 0,
            openAirAnima: 0
        };

        this._building = null;

        this._playersUID = null;

        this._rotateGlobeId = null;
        this._rotateGlobeX = 180
        this._rotateGlobeY = 0;

        this._openAMAP = openAMAP;
        // this._krigingCan = krigingCan;

        const CompassConfig = {
            defaultResetView: Cesium.Rectangle.fromDegrees(80, 22, 130, 50),
            enableCompass: true,
            enableZoomControls: false,
            enableDistanceLegend: false,
            enableCompassOuterRing: true
        };

        let ter;
        openMAPWORLD.tdServer || openAMAP.amapServer ? null : Cesium.Ion.defaultAccessToken = this.ces_token;
        openTerrain ? ter = Cesium.Terrain.fromWorldTerrain({
            requestVertexNormals: true
        }) : ter = undefined;

        /**@description cesium view controller */
        this._viewer = new Cesium.Viewer(container, {
            terrain: ter,
            selectionIndicator: false,
            infoBox: false,
            geocoder: false,
            homeButton: false,
            fullscreenButton: false,
            animation: openClock,
            sceneModePicker: false,
            timeline: openClock,
            navigationHelpButton: false,
            baseLayerPicker: !openMAPWORLD.tdServer || !openAMAP.amapServer,
            shouldAnimate: true,
            shadows: true
        });

        this._handler = new Cesium.ScreenSpaceEventHandler(this._viewer.canvas);
        this._scene = this._viewer.scene;
        this._scene.globe.enableLighting = realLight;
        this._scene.postProcessStages.fxaa.enabled = true;
        this._viewer._cesiumWidget._creditContainer.style.display = 'none';
        this._cescan = cesCan;
        this._enCan = enableCan;
        _cescan = cesCan;
        _enCan = enableCan;

        /**@description {dev} */
        // this._viewer.scene.skyAtmosphere.hueShift = -0.5;
        // this._viewer.scene.skyAtmosphere.brightness = 0.5;

        /**@description load amap */
        if (openAMAP.amapServer) {
            this._viewer.imageryLayers.addImageryProvider(new Cesium.UrlTemplateImageryProvider({
                url: `https://webrd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}`,
                style: "default",
                format: "image/jpeg",
                minimumLevel: 3,
                maximumLevel: 18,
                credit: 'Amap',
                rectangle: Cesium.Rectangle.fromDegrees(123.266990, 41.654311, 123.627965, 41.980070)  // cn.sy
            }));
        };

        /**@description load MAPWORLD */
        if (openMAPWORLD.tdServer) {

            /**@description storey: map */
            this._viewer.imageryLayers.add(new Cesium.ImageryLayer(new Cesium.WebMapTileServiceImageryProvider({
                url: `http://t0.tianditu.com/${openMAPWORLD.type}/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=${openMAPWORLD.type.split('_')[0]}&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=${this.td_token}`,
                layer: "tdtBasicLayer",
                style: "default",
                format: "image/jpeg",
                tileMatrixSetID: "GoogleMapsCompatible",
                subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
                tilingScheme: new Cesium.WebMercatorTilingScheme(),
                minimumLevel: 0,
                maximumLevel: 18,
                credit: 'Tianditu',
                // rectangle : Cesium.Rectangle.fromDegrees(123.266990, 41.654311, 123.627965, 41.980070)  // cn.sy
            })));

            /**@description storey: map place name */
            this._viewer.imageryLayers.add(new Cesium.ImageryLayer(new Cesium.WebMapTileServiceImageryProvider({
                url: `http://t0.tianditu.com/${openMAPWORLD.tag}/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=${openMAPWORLD.tag.split('_')[0]}&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=${this.td_token}`,
                layer: "tdtBasicLayer",
                style: "default",
                format: "image/jpeg",
                tileMatrixSetID: "GoogleMapsCompatible",
                subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
                tilingScheme: new Cesium.WebMercatorTilingScheme(),
                minimumLevel: 0,
                maximumLevel: 18,
                credit: 'Tianditu',
                // rectangle : Cesium.Rectangle.fromDegrees(123.266990, 41.654311, 123.627965, 41.980070)  // cn.sy
            })));

            /**@description tile: tiles map */
            // this._viewer.imageryLayers.add(new Cesium.ImageryLayer(new Cesium.UrlTemplateImageryProvider({
            //     url: this.static_http + this.path_models + 'tiles/' + '{z}/{x}/{y}.png',
            //     minimumLevel: 3,
            //     maximumLevel: 18,
            //     tilingScheme: new Cesium.WebMercatorTilingScheme(),
            //     rectangle : Cesium.Rectangle.fromDegrees(123.266990, 41.654311, 123.627965, 41.980070)    // cn.sy
            // })));
        };

        /**@description use open clock */
        if (openClock) {
            this.start = Cesium.JulianDate.fromDate(new Date());
            this.totalSeconds = 10;
            this.stop = Cesium.JulianDate.addSeconds(
                this.start,
                this.totalSeconds,
                new Cesium.JulianDate(),
            );
            this._viewer.clock.startTime = this.start.clone();
            this._viewer.clock.stopTime = this.stop.clone();
            this._viewer.clock.currentTime = this.start.clone();
            this._viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
            this._viewer.timeline.zoomTo(this.start, this.stop);
        };

        /**@description use compass */
        new CesiumNavigation(this._viewer, CompassConfig);

        /**@description use socket.io */
        this._io = new Socket();
        this._io.listenStartGame.call(this);

        this._io.joinGameWorld();
        this._io.getUiAirCronJob();
        this._io.getUiStatusValue();
        /**@description ready start game  */
        this._io.readyStartGame();


        /**@description open globe */
        this.openRotateGlobe();

        this._io.getPlayerUID();

        this._io.listenBuildingHeartbeatDetection.call(this);

    };

    /** ------------------- ^Core Function ---------------- */

    /**
     * @function openRotateGlobe
     * -opne globe controller
     */
    async openRotateGlobe() {
        this._rotateGlobeX = this._rotateGlobeX + 0.1;
        if (this._rotateGlobeX >= 178.5) {
            this._rotateGlobeX = -180;
        }
        this._scene.camera.setView({
            destination: Cesium.Cartesian3.fromDegrees(this._rotateGlobeX, this._rotateGlobeY, 25000000)
        });

        this._rotateGlobeId = requestAnimationFrame(this.openRotateGlobe.bind(this));
    };

    /**
     * @function closeRotateGlobe
     * -close globe controller
     */
    async closeRotateGlobe() {
        this._rotateGlobeX = 180;
        this._rotateGlobeY = 0;

        cancelAnimationFrame(this._rotateGlobeId);
        this._rotateGlobeId = null;

        /**@description open ues AMAP */
        this.openAMAP(this._openAMAP, async result => {
            if (result) {

                new ListenVueEventsController().Events.call(this);

                await this.GameRegionRadiationController();

                await this.CreateGeoJsonRivers('river.jpg', 'river.json');

                await this.GameRegionMapTagController();

                await this.StaticModelGltfPartialCoordinateInit(false, 'flyTo', 200, 500, 'STATIC-MODEL-GLTF-0pNRLM', [])

                await this.StaticModelGltfCoordinateInit(false, 'flyTo', 200, 500, 'STATIC-MODEL-GLTF-Al2OIj', ['STATIC-MODEL-GLTF-Al2OIj', 'STATIC-MODEL-GLTF-61LE2O', 'STATIC-MODEL-GLTF-uTNX5Z']);

                await this.GameAirdropModelGltfOfSpacecraftController();

                await this.GameAirdropBoxModelGltfOfSpacecraftController();

                await this.PlayersModelGltfCoordinateInit(true, 'flyTo', 200, 500, puid);

                await this.GameRegionClickOnTheInteractionEvent(puid, 'LEFT_CLICK');

                await this.GameCheckSafeHouseIsBuildingRemainingTime();

                await this.GetGameIndustryCount();

                await this.GameCheckSafeHouseIsBuilding();

            };
        });

    };

    async GetGameIndustryCount() {
        return new Promise((resolve, reject) => {
            axios.get(`${proxy.development.API}/api/v1/temp_api/game/getPlayerIndustryCount?playerUID=${puid}`)
                .then(response => {
                    if (response.data.code === 200) {
                        SceneVueInteractiveSubscription.publish('ui_Industry_count', response.data.message);
                        resolve();
                    };
                }).catch(error => {
                    console.error('Error:', error);
                    reject(error);
                });
        });
    };

    async GameCheckSafeHouseIsBuildingRemainingTime() {
        return new Promise((resolve, reject) => {
            axios.get(`${proxy.development.API}/api/v1/temp_api/game/getPlayerBuildingRemainingTime?playerUID=${puid}`)
                .then(response => {
                    if (response.data.code === 200) {
                        SceneVueInteractiveSubscription.publish('build_timer', response.data.message.totalMilliseconds);
                        resolve();
                    };
                    if (response.data.code === 900) {
                        SceneVueInteractiveSubscription.publish('build_timer', response.data.message);
                        resolve();
                    };
                    if (response.data.code === 800) {
                        SceneVueInteractiveSubscription.publish('build_timer', response.data.message);
                        resolve();
                    };
                }).catch(error => {
                    console.error('Error:', error);
                    reject(error);
                });
        });
    };

    async GameCheckSafeHouseIsBuilding() {
        const model = this.GameRegionClickOnTheInteractionEventFindEntity(this._scene.primitives, "STATIC-MODEL-GLTF-xxzEYY");

        axios.post(`${proxy.development.API}/api/v1/temp_api/game/playersBuildingStatus`, {
            playerUID: puid,
            type: ''
        }).then(response => {
            if (response.data.code === 200) {
                const data = JSON.parse(response.data.message.player_status_build)
                model.color = Cesium.Color.fromAlpha(Cesium.Color.WHITE, data.playerBuild);

                this._building = data.playerBuild;
            };
        }).catch(error => {
            console.error('Error:', error);
        });

    };

    async GameSafeHouseBuildingTimer() {
        return new Promise((resolve, reject) => {

            axios.get(`${proxy.development.API}/api/v1/temp_api/game/getUiBuildTimer?playerUID=${puid}`)
                .then(response => {
                    if (response.data.code === 200) {
                        SceneVueInteractiveSubscription.publish('build_timer', response.data.message);
                        this.GetGameIndustryCount();
                        resolve();
                    };
                    if (response.data.code === 700) {
                        SceneVueInteractiveSubscription.publish('ui_build_message', response.data.message);

                        resolve();
                    };
                    if (response.data.code === 900) {
                        SceneVueInteractiveSubscription.publish('ui_build_message', response.data.message);
                        resolve();
                    };
                    if (response.data.code === 800) {
                        SceneVueInteractiveSubscription.publish('ui_build_done', response.data.message);
                        resolve();
                    };
                }).catch(error => {
                    console.error('Error:', error);
                    reject(error);
                });

        });
    };

    async ListenVueUICameraEvents() {
        SceneVueInteractiveSubscription.subscribe('ui_camera', num => {
            switch (num) {
                case 1:
                    this._ui_camera_status.air = 0;
                    this.UiCameraSwitchGameRegionMap('all');
                    break;
                case 2:
                    if (!this._ui_camera_status.openAirAnima) return;
                    this._ui_camera_status.air = 1;
                    this.UiCameraSwitchGameModelMoveAnima('AIR-MODEL-UcO9UC');
                    break;
                case 4:
                    this._ui_camera_status.air = 0;
                    this.UiCameraSwitchGameModelMoveAnima(puid);
                    break;
            };
        });
    };

    /**
     * 
     * @param {*} openAMAP 
     * @param {*} callback 
     * @function openAMAP
     * -use open amap
     */
    @Center
    async openAMAP(openAMAP, callback) {
        window._AMapSecurityConfig = {
            securityJsCode: this.amap_secret,
        };

        this._AMap = await AMapLoader.load({
            key: this.amap_key,
            version: '2.0',
            plugins: ['AMap.Driving', 'AMap.Weather']
        });
        this._map = new this._AMap.Map(openAMAP.can, {
            viewMode: '2D',
            center: openAMAP.center    // map center find any gaode coordinate system you want
        });

        callback(true);
    };

    /**
     * 
     * @param {*} mode 
     * @returns 
     * @function CameraController
     * -camera controller init
     */
    CameraController(mode) {
        return new Promise((resolve, reject) => {
            this._scene.screenSpaceCameraController.enableRotate = mode.Ro;
            this._scene.screenSpaceCameraController.enableTranslate = mode.Tr;
            this._scene.screenSpaceCameraController.enableZoom = mode.Zo;
            this._scene.screenSpaceCameraController.enableTilt = mode.Ti;
            this._scene.screenSpaceCameraController.enableLook = mode.Lo;
            this._scene.screenSpaceCameraController.maximumZoomDistance = 2500;

            resolve();
        });
    };

    /**
     * 
     * @param {*} isFly 
     * @param {*} howFly 
     * @param {*} metre 
     * @param {*} time 
     * @param {*} flyId 
     * @param {*} anima 
     * @returns 
     * @function
     * -gltf static model coordinate init
     * -A large number of models are adopted, Cesium.Model() Bottom level API rendering optimization.
     * -global coordinate system
     */
    StaticModelGltfCoordinateInit(isFly, howFly, metre, time, flyId, anima) {
        return new Promise(async (resolve, reject) => {

            const response = await axios.get(`${proxy.development.API}/api/v1/temp_api/game/getAllGameCoordinate?sw=StaticModelGltf`);

            for (const item of response.data.message) {
                const position = Cesium.Cartesian3.fromDegrees(
                    item.coor.lon,
                    item.coor.lat,
                    item.coor.h
                );
                const heading = Cesium.Math.toRadians(item.ori.h);  // Orientation angle
                const pitch = item.ori.p;
                const roll = item.ori.r;
                const orientation = Cesium.Quaternion.fromHeadingPitchRoll(
                    new Cesium.HeadingPitchRoll(heading, pitch, roll)
                );
                const modelMatrix = Cesium.Matrix4.fromTranslationRotationScale(
                    new Cesium.TranslationRotationScale(
                        position,
                        orientation,
                        new Cesium.Cartesian3(1.0, 1.0, 1.0)
                    )
                );

                const gltfCallback = (gltf) => {
                    // console.log("gltf static model anima map:", gltf.animations);
                };

                const modelPromise = Cesium.Model.fromGltfAsync({
                    id: item.uid,
                    url: this.static_http + this.path_models + item.gltf,
                    modelMatrix: modelMatrix,
                    // minimumPixelSize: item.sc.min,
                    // maximumScale: item.sc.max,
                    scale: item.sc.scale,
                    gltfCallback: gltfCallback
                });

                const model = await modelPromise;

                this._scene.primitives.add(model);

                this._StaticModelGltfCoordinateModelMap.set(item.uid, model);

            };

            if (!isFly) {

                resolve();

            } else {

                const model = this._StaticModelGltfCoordinateModelMap.get(flyId);
                const modelMatrix = model.modelMatrix;

                const position = Cesium.Matrix4.getTranslation(modelMatrix, new Cesium.Cartesian3());
                const cartographic = Cesium.Cartographic.fromCartesian(position);

                const lon = Cesium.Math.toDegrees(cartographic.longitude);
                const lat = Cesium.Math.toDegrees(cartographic.latitude);
                const h = cartographic.height;

                let process = howFly ? 'flyTo' : 'setView';
                await this._viewer.camera[process]({
                    destination: Cesium.Cartesian3.fromDegrees(
                        lon,
                        lat,
                        metre
                    ),
                    orientation: {
                        heading: Cesium.Math.toRadians(0.0),
                        pitch: Cesium.Math.toRadians(-90.0),
                    },
                    // duration: 20,
                    // flyOverLongitude: Cesium.Math.toRadians(60.0)
                });

                this.StaticModelGltfCameraArrivingAtCoordinates(model, time, metre, () => {
                    /**@description close camera lock */
                    this.CameraController({
                        Ro: true,
                        Tr: true,
                        Zo: true,
                        Ti: true,
                        Lo: true
                    });
                });

                resolve();
            };

            /**@description static model gitf anima map */
            anima.length > 0 ? this.StaticModelGltfAnimaController(2, 0.5, anima) : null;

        });
    };

    /**
     * 
     * @param {*} isFly 
     * @param {*} howFly 
     * @param {*} metre 
     * @param {*} time 
     * @param {*} flyId 
     * @param {*} anima 
     * @returns 
     * @function
     * -gltf static partial model coordinate init
     * -A large number of models are adopted, Cesium.Model() Bottom level API rendering optimization.
     * -Team up in local coordinate system
     */
    StaticModelGltfPartialCoordinateInit(isFly, howFly, metre, time, flyId, anima) {

        return new Promise((resolve, reject) => {

            axios.get(`${proxy.development.API}/api/v1/temp_api/game/getAllGameCoordinate?sw=StaticModelGltfPartial`)
                .then(response => {
                    if (response.data.code === 200) {

                        response.data.message.forEach(async item => {
                            const origin = Cesium.Cartesian3.fromDegrees(item.center.lon, item.center.lat, item.center.h);
                            const enuToFixedFrame = Cesium.Transforms.eastNorthUpToFixedFrame(origin);

                            for (const item_child of item.model) {
                                const positionOffset = new Cesium.Cartesian3(
                                    item_child.offset.east || 0,  // Eastward offset (m)
                                    item_child.offset.north || 0, // North offset (m)
                                    item_child.offset.up || 0     // Sky direction offset (m)
                                );

                                const worldPosition = Cesium.Matrix4.multiplyByPoint(
                                    enuToFixedFrame,
                                    positionOffset,
                                    new Cesium.Cartesian3()
                                );

                                const heading = Cesium.Math.toRadians(item_child.ori.h);  // Orientation angle
                                const pitch = item_child.ori.p;
                                const roll = item_child.ori.r;
                                const orientation = Cesium.Quaternion.fromHeadingPitchRoll(
                                    new Cesium.HeadingPitchRoll(heading, pitch, roll)
                                );
                                const modelMatrix = Cesium.Matrix4.fromTranslationRotationScale(
                                    new Cesium.TranslationRotationScale(
                                        worldPosition,
                                        orientation,
                                        new Cesium.Cartesian3(1.0, 1.0, 1.0)
                                    )
                                );

                                const gltfCallback = (gltf) => {
                                    // console.log("gltf static model anima map:", gltf.animations);
                                };

                                const modelPromise = Cesium.Model.fromGltfAsync({
                                    id: item_child.uid,
                                    url: this.static_http + this.path_models + item_child.gltf,
                                    modelMatrix: modelMatrix,
                                    // minimumPixelSize: item_child.sc.min,
                                    // maximumScale: item_child.sc.max,
                                    scale: item_child.sc.scale,
                                    gltfCallback: gltfCallback
                                });

                                const model = await modelPromise;

                                this._scene.primitives.add(model);

                                this._StaticModelGltfPartialCoordinateModelMap.set(item_child.uid, model);

                            };

                            if (!isFly) {

                                resolve();

                            } else {

                                const model = this._StaticModelGltfPartialCoordinateModelMap.get(flyId);
                                const modelMatrix = model.modelMatrix;

                                const position = Cesium.Matrix4.getTranslation(modelMatrix, new Cesium.Cartesian3());
                                const cartographic = Cesium.Cartographic.fromCartesian(position);

                                const lon = Cesium.Math.toDegrees(cartographic.longitude);
                                const lat = Cesium.Math.toDegrees(cartographic.latitude);
                                const h = cartographic.height;

                                let process = howFly ? 'flyTo' : 'setView';
                                await this._viewer.camera[process]({
                                    destination: Cesium.Cartesian3.fromDegrees(
                                        lon,
                                        lat,
                                        metre
                                    ),
                                    orientation: {
                                        heading: Cesium.Math.toRadians(0.0),
                                        pitch: Cesium.Math.toRadians(-90.0),
                                    },
                                    // duration: 20,
                                    // flyOverLongitude: Cesium.Math.toRadians(60.0)
                                });

                                this.StaticModelGltfCameraArrivingAtCoordinates(model, time, metre, () => {
                                    /**@description close camera lock */
                                    this.CameraController({
                                        Ro: true,
                                        Tr: true,
                                        Zo: true,
                                        Ti: true,
                                        Lo: true
                                    });
                                });

                                resolve();
                            };

                            /**@description static model gitf anima map */
                            anima.length > 0 ? this.StaticModelGltfAnimaController(1, 1.0, anima) : null;

                        });

                    };
                })
                .catch(error => {
                    console.error('Error:', error);
                    reject(error);
                });
        });
    };

    /**
     * 
     * @param {*} model 
     * @param {*} isFly 
     * @param {*} howFly 
     * @param {*} metre 
     * @param {*} time 
     * @param {*} flyId 
     * @returns 
     */
    PlayersModelGltfCoordinateInit(isFly, howFly, metre, time, flyId) {

        return new Promise((resolve, reject) => {

            axios.get(`${proxy.development.API}/api/v1/temp_api/game/startGame?playerUID=${puid}`)
                .then(async response => {
                    if (response.data.code === 200) {
                        const body = JSON.parse(response.data.message.body.players_info);
                        const gpsPoint = {
                            longitude: body.playerLong,
                            latitude: body.playerLat
                        };
                        const gcj02Point = new this._AMap.LngLat(gpsPoint.longitude, gpsPoint.latitude);

                        const data = [{
                            ori: {
                                h: 0
                            },
                            coor: {
                                /**@description {pro} */
                                lon: gcj02Point.lng,
                                lat: gcj02Point.lat,
                                /**@description {dev} */
                                // lon: null,
                                // lat: null,
                                h: 0
                            },
                            uid: body.playerUID,
                            pname: body.playerName,
                            gltf: 'character-skate-boy.glb'
                        }];

                        data.forEach(item => {
                            _playerCoordinatesA = {
                                startLon: item.coor.lon,
                                startLat: item.coor.lat
                            };
                            const position = Cesium.Cartesian3.fromDegrees(
                                item.coor.lon,
                                item.coor.lat,
                                item.coor.h
                            );
                            const heading = Cesium.Math.toRadians(item.ori.h);
                            const pitch = 0;
                            const roll = 0;
                            const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
                            const orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr);
                            const entity = this._viewer.entities.getById(item.uid);
                            if (entity) this._viewer.entities.removeById(item.uid);
                            this._viewer.entities.add({
                                id: item.uid,
                                name: item.pname,
                                orientation: orientation,
                                position: position,
                                model: {
                                    uri: this.static_http + this.path_models + item.gltf,
                                    clampAnimations: false,
                                    runAnimations: true,
                                    minimumPixelSize: 100,
                                    maximumScale: 50,
                                    show: true
                                },
                            });
                        });

                        if (!isFly) {

                            resolve();

                        } else {


                            const lon = Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(this._viewer.entities.getById(flyId).position.getValue()).longitude);
                            const lat = Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(this._viewer.entities.getById(flyId).position.getValue()).latitude);

                            let process = howFly ? 'flyTo' : 'setView';
                            await this._viewer.camera[process]({
                                destination: Cesium.Cartesian3.fromDegrees(
                                    lon,
                                    lat,
                                    metre
                                ),
                                orientation: {
                                    heading: Cesium.Math.toRadians(0.0),
                                    pitch: Cesium.Math.toRadians(-90.0),
                                },
                                // duration: 20,
                                // flyOverLongitude: Cesium.Math.toRadians(60.0)
                            });

                            this.PlayerModelGltfCameraArrivingAtCoordinates(time, metre, flyId, () => {

                                // plaeyr camera
                                this.CameraController({
                                    Ro: true,
                                    Tr: true,
                                    Zo: false,
                                    Ti: true,
                                    Lo: true
                                });

                                // open player move anima
                                this._io.listenTimerPlayersInfo(this);

                                // open ui switch camera
                                this.ListenVueUICameraEvents();

                            });

                            resolve();

                        };
                    };
                })
                .catch(error => {
                    reject(error);
                });
        });
    };

    /**
     * 
     * @param {*} time 
     * @param {*} metre 
     * @param {*} flyId 
     * @param {*} callback 
     */
    async PlayerModelGltfCameraArrivingAtCoordinates(time, metre, flyId, callback) {
        const removeListenerPlayersModelGltfCoordinateInit = this._viewer.camera.changed.addEventListener(() => {
            const model = this._viewer.entities.getById(flyId);
            const cameraPosition = this._viewer.camera.position;
            const modelPosition = model.position.getValue(Cesium.JulianDate.now());
            const distance = Cesium.Cartesian3.distance(cameraPosition, modelPosition);
            if (distance < metre + 100) {
                setTimeout(() => {
                    /**@description tracked players */
                    this._viewer.trackedEntity = model;
                    this._viewer.trackedEntity.viewFrom = new Cesium.Cartesian3(-20, -90, 20);
                }, time);
                removeListenerPlayersModelGltfCoordinateInit();

                callback();
            };
        });
    };

    /**
     * 
     * @param {*} position 
     * @param {*} time 
     */
    async PlayerGltfMovePathAnima(position, time) {

        position.forEach(pos => {
            const entity = this._viewer.entities.getById(pos.uid);
            if (!entity) return;

            const property = new Cesium.SampledPositionProperty();
            entity.position = property;

            const startTime = Cesium.JulianDate.now();
            const duration = time;
            const stopTime = Cesium.JulianDate.addSeconds(startTime, duration, new Cesium.JulianDate());

            const positionStartPoint = Cesium.Cartesian3.fromDegrees(pos.startLon, pos.startLat, pos.startH);
            const positionStopPoint = Cesium.Cartesian3.fromDegrees(pos.stopLon, pos.stopLat, pos.stopH);

            property.addSample(
                startTime,
                positionStartPoint
            );

            property.addSample(
                stopTime,
                positionStopPoint
            );

            // this._viewer.clock.startTime = startTime.clone();
            // this._viewer.clock.stopTime = stopTime.clone();
            // this._viewer.clock.currentTime = startTime.clone();
            // this._viewer.clock.clockRange = Cesium.ClockRange.CLAMPED;

            entity.orientation = new Cesium.VelocityOrientationProperty(property);

            const checkAnimationComplete = () => {
                // console.log(Cesium.JulianDate.compare('playerAnima倒计时', this._viewer.clock.currentTime, stopTime));
                if (Cesium.JulianDate.compare(
                    this._viewer.clock.currentTime,
                    stopTime
                ) >= 0) {
                    console.log('playerAnima执行结束');
                    this._viewer.clock.onTick.removeEventListener(checkAnimationComplete);

                    entity.position = Cesium.Cartesian3.fromDegrees(pos.stopLon, pos.stopLat, pos.stopH);

                    // open player move anima
                    this._io.listenTimerPlayersInfo(this);
                };
            };

            this._viewer.clock.onTick.addEventListener(checkAnimationComplete);

        });

    };

    /**
     * 
     * @param {*} model 
     * @param {*} time 
     * @param {*} metre 
     * @param {*} callback 
     * @function
     * -static model camera AAC
     */
    async StaticModelGltfCameraArrivingAtCoordinates(model, time, metre, callback) {
        model.readyEvent.addEventListener(() => {
            this._boundingSphere = model.boundingSphere;
            this._center = this._boundingSphere.center;

            const removeListenerStaticModelGltfCameraArrivingAtCoordinates = this._viewer.camera.changed.addEventListener(() => {
                let height = this._viewer.camera.positionCartographic.height;
                if (height <= metre + 100) {
                    setTimeout(() => {
                        // ......
                    }, time);
                    removeListenerStaticModelGltfCameraArrivingAtCoordinates();

                    callback();
                };
            });

        });
    };

    /**
     * 
     * @param {*} index 
     * @param {*} speed 
     * @function
     * -static model gitf anima map
     */
    async StaticModelGltfAnimaController(index, speed, anima) {
        anima.forEach(uid => {
            const model = this._StaticModelGltfCoordinateModelMap.get(uid);
            model.activeAnimations.add({
                index: index,
                loop: Cesium.ModelAnimationLoop.REPEAT,
                multiplier: speed
            });
        });
    };

    /**
     * 
     * @param {*} 
     * @returns 
     */
    async GameRegionMapTagController() {
        return new Promise((resolve, reject) => {
            const billboards = this._scene.primitives.add(new Cesium.BillboardCollection());

            axios.get(`${proxy.development.API}/api/v1/temp_api/game/getAllGameCoordinate?sw=GameRegionMapTag`)
                .then(response => {
                    if (response.data.code === 200) {
                        response.data.message.forEach(item => {
                            billboards.add({
                                id: item.uid,
                                name: item.name,
                                image: this.static_http + this.path_img + item.png,
                                position: Cesium.Cartesian3.fromDegrees(item.lon, item.lat, item.h),
                                scale: 0.5,
                                show: item.so
                                // sizeInMeters: true
                            });
                            resolve();
                        });
                    };
                })
                .catch(error => {
                    console.error('Error:', error);
                    reject(error);
                });
        });
    };

    /**
     * 
     * @param {*} mouseKey 
     * @returns 
     * @function
     * -click event
     */
    async GameRegionClickOnTheInteractionEvent(puid, mouseKey) {
        return new Promise((resolve, reject) => {
            this._handler.setInputAction(async movement => {
                const feature = this._scene.pick(movement.position);

                if (!mouseKey) return reject('mouseKey undefined');
                if (!Cesium.defined(feature)) return reject('Error feature');

                const clickType = feature.primitive.constructor.name;
                const uid = feature.primitive.id;

                if (uid?._id && uid?._id === puid) {
                    SceneVueInteractiveSubscription.publish('ui_open_backpack', puid);
                };

                switch (uid) {
                    case 'STATIC-MODEL-GLTF-xxzEYY':
                        SceneVueInteractiveSubscription.publish('ui_open_building', puid);
                        break;
                    case 'STATIC-MODEL-GLTF-PARTIAL-vjRCoj':
                        EnableGameWorldSceneInit.WoodsScene(() => {
                            socket.emit('IO_ROOM_ENABLE3D_SWITCH_SCENE', {
                                id: puid,
                                sw: 'building-type-p.glb'
                            });
                        });
                        break;
                    case 'STATIC-MODEL-GLTF-PARTIAL-wVEd7y':
                        EnableGameWorldSceneInit.WoodsScene(() => {
                            socket.emit('IO_ROOM_ENABLE3D_SWITCH_SCENE', {
                                id: puid,
                                sw: 'tree-large.glb'
                            });
                        });
                        break;
                    case 'STATIC-MODEL-GLTF-PARTIAL-uuhC6B':
                        EnableGameWorldSceneInit.WoodsScene(() => {
                            socket.emit('IO_ROOM_ENABLE3D_SWITCH_SCENE', {
                                id: puid,
                                sw: 'rock-large.glb'
                            });
                        });
                        break;
                    case 'STATIC-MODEL-GLTF-ksoMY1':
                        EnableGameWorldSceneInit.WoodsScene(() => {
                            socket.emit('IO_ROOM_ENABLE3D_SWITCH_SCENE', {
                                id: puid,
                                sw: 'hole.glb'
                            });
                        });
                        break;
                };

                switch (clickType) {
                    case 'Billboard':  // tag
                        const bpr = this.GameRegionClickOnTheInteractionEventFindEntity(feature.collection, uid);

                        const tagPosition = await this.Cartesian3TurnCartographic(bpr.position);
                        const playerPosition = await this.Cartesian3TurnCartographic(this._viewer.entities.getById(puid).position.getValue(Cesium.JulianDate.now()));

                        this.UiCameraSwitchGameRegionMap('', tagPosition);
                        this.GameRegionGCJ02PathPlanningNavigation({ startPoint: [playerPosition.longitude, playerPosition.latitude], endPoint: [tagPosition.longitude, tagPosition.latitude] });
                        break;
                    case 'Model':   // model
                        this.GameRegionClickOnTheInteractionEventFindResetEntity();
                        const mpr = this.GameRegionClickOnTheInteractionEventFindEntity(this._scene.primitives, uid);
                        // mpr.color = Cesium.Color.RED;
                        mpr.silhouetteSize = 3;
                        break;
                };

                const model = this.GameRegionClickOnTheInteractionEventFindEntity(this._scene.primitives, "STATIC-MODEL-GLTF-xxzEYY");
                model.color = Cesium.Color.fromAlpha(Cesium.Color.WHITE, this._building);

            }, Cesium.ScreenSpaceEventType[mouseKey]);

            resolve();
        });
    };

    /**
     * 
     * @param {*} entity 
     * @param {*} uid 
     * @returns 
     * @function
     * -click event find entity
     */
    GameRegionClickOnTheInteractionEventFindEntity(entity, uid) {
        for (let i = 0; i < entity.length; i++) {
            const primitive = entity.get(i);
            if (primitive.id === uid) {
                return primitive;
            };
        };
    };

    /**
     * @function
     * -click event reset entity
     */
    GameRegionClickOnTheInteractionEventFindResetEntity() {
        for (let i = 0; i < this._scene.primitives.length; i++) {
            const primitive = this._scene.primitives.get(i);
            primitive.color = new Cesium.Color();
            primitive.silhouetteSize = 0;
        };
    };

    /**
     * 
     * @param {*} position 
     * @returns 
     * @function
     * -Dilka turn lon lat
     */
    Cartesian3TurnCartographic(position) {
        return new Promise((resolve, reject) => {
            const cartographic = Cesium.Cartographic.fromCartesian(position);
            const longitude = Cesium.Math.toDegrees(cartographic.longitude);
            const latitude = Cesium.Math.toDegrees(cartographic.latitude);

            resolve({
                longitude,
                latitude
            });
        });
    };

    /**
     * 
     * @param {*} l 
     * @returns 
     * @function
     * -game region navigation
     */
    GameRegionGCJ02PathPlanningNavigation(l) {
        return new Promise(async (resolve, reject) => {
            if (!this._AMap || !this._map) return reject();
            try {
                const startPoint = new this._AMap.LngLat(l.startPoint[0], l.startPoint[1]);
                const endPoint = new this._AMap.LngLat(l.endPoint[0], l.endPoint[1]);
                this._map.clearMap();
                const driving = new this._AMap.Driving({
                    map: this._map
                });
                driving.search(startPoint, endPoint, (status, result) => {
                    if (status === 'complete') {
                        const steps = result.routes[0].steps;
                        let arr = [];
                        let path = [];
                        let cesPath = [];
                        steps.map(step => {
                            arr = arr.concat(step.path);
                        });
                        arr.map(item => {
                            path.push([item.lng, item.lat]);
                        });
                        path.map(item => {
                            cesPath.push(Cesium.Cartesian3.fromDegrees(item[0], item[1]));
                        });

                        this.CreateLightRoad('trajectory.png', '', false, cesPath);
                    };
                });
                resolve();
            } catch (error) {
                reject(error);
            };
        });
    };

    /**
     * 
     * @param {*} imgPath 
     * @param {*} jsonPath 
     * @param {*} isGeoJson 
     * @param {*} path 
     * @returns 
     * @function
     * - create light road
     */
    CreateLightRoad(imgPath, jsonPath, isGeoJson, path) {
        return new Promise(async (resolve, reject) => {
            if (!imgPath) return reject('imgPath undefined');
            for (let i = 0; i < this._scene.primitives.length; i++) {
                const primitives = this._scene.primitives.get(i);
                if (primitives.constructor.name === 'Primitive') {
                    this._scene.primitives.remove(primitives);
                };
            };

            let source = `czm_material czm_getMaterial(czm_materialInput materialInput)
            {
                czm_material material = czm_getDefaultMaterial(materialInput);
                vec2 st = materialInput.st;
                vec4 colorImage = texture(image, vec2(fract((st.s - speed * czm_frameNumber * 0.001)), st.t));
                material.alpha = colorImage.a * color.a;
                material.diffuse = colorImage.rgb * 1.5 ;
                return material;
            }`;
            let position = [];
            if (isGeoJson) {
                const waterGeoJson = await Cesium.GeoJsonDataSource.load(this.static_http + this.path_models + jsonPath);
                waterGeoJson.entities.values.forEach(e => {
                    position.push(e.polyline.positions.getValue(Cesium.JulianDate.now()));
                });
            };
            const polyline = new Cesium.PolylineGeometry({
                positions: isGeoJson ? position[0] : path,
                width: 10,
                vertexFormat: Cesium.PolylineMaterialAppearance.VERTEX_FORMAT,
            });
            const geometry = Cesium.PolylineGeometry.createGeometry(polyline);
            const instance = new Cesium.GeometryInstance({
                geometry,
            });
            const material = new Cesium.Material({
                fabric: {
                    uniforms: {
                        color: Cesium.Color.fromCssColorString("#7ffeff"),
                        image: this.static_http + this.path_img + imgPath,
                        speed: 10,
                    },
                    source,
                },
                translucent: function () {
                    return true;
                },
            });
            const appearance = new Cesium.PolylineMaterialAppearance();
            appearance.material = material;
            const primitive = new Cesium.Primitive({
                geometryInstances: instance,
                appearance,
                asynchronous: false,
            });

            this._viewer.scene.primitives.add(primitive);

            resolve();
        });
    };

    /**
     * 
     * @param {*} so 
     */
    GameRegionMapTagIconController(so) {
        for (let i = 0; i < this._scene.primitives.length; i++) {
            const primitives = this._scene.primitives.get(i);
            if (primitives instanceof Cesium.BillboardCollection) {
                for (let i = 0; i < primitives.length; i++) {
                    const billboard = primitives.get(i);
                    billboard.show = so;
                };
            };
        };
    };

    /**
     * 
     * @param {*} unlock 
     */
    GameRegionMapLockTagIconController(unlock) {
        for (let i = 0; i < this._scene.primitives.length; i++) {
            const primitives = this._scene.primitives.get(i);
            if (primitives instanceof Cesium.BillboardCollection) {
                for (let i = 0; i < primitives.length; i++) {
                    const billboard = primitives.get(i);
                    if (billboard.id === 'TAG-LOCK-1-oCcLHS') {
                        if (unlock === 1) {
                            billboard.image = this.static_http + this.path_img + 'lock_unlocked.png';
                        } else {
                            billboard.image = this.static_http + this.path_img + 'lock.png'
                        };
                    };
                    if (billboard.id === 'TAG-LOCK-2-CFkRE6') {
                        if (unlock === 2) {
                            billboard.image = this.static_http + this.path_img + 'lock_unlocked.png';
                        } else {
                            billboard.image = this.static_http + this.path_img + 'lock.png';
                        };
                    };
                };
            };
        };
    };

    /**
     * 
     * @param {*} 
     * @returns 
     */
    GameAirdropModelGltfOfSpacecraftController() {
        return new Promise(async (resolve, reject) => {
            const path = {
                leadTime: 0,
                trailTime: 60,
                width: 4,
                material: new Cesium.PolylineGlowMaterialProperty({
                    color: Cesium.Color.RED
                })
            };

            axios.get(`${proxy.development.API}/api/v1/temp_api/game/getAllGameCoordinate?sw=GameAirdropModelGltfOfSpacecraft`)
                .then(response => {
                    if (response.data.code === 200) {
                        response.data.message.forEach(item => {
                            const position = Cesium.Cartesian3.fromDegrees(
                                item.coor.lon,
                                item.coor.lat,
                                item.coor.h
                            );
                            const heading = Cesium.Math.toRadians(item.ori.h);
                            const pitch = 0;
                            const roll = 0;
                            const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
                            const orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr);
                            const entity = this._viewer.entities.getById(item.uid);
                            if (entity) this._viewer.entities.removeById(item.uid);
                            this._viewer.entities.add({
                                id: item.uid,
                                name: item.gname,
                                orientation: orientation,
                                position: position,
                                model: {
                                    uri: this.static_http + this.path_models + item.gltf,
                                    clampAnimations: false,
                                    runAnimations: true,
                                    minimumPixelSize: 100,
                                    maximumScale: 50,
                                    show: item.show
                                },
                                path: path
                            });
                        });
                        resolve();
                    };
                })
                .catch(error => {
                    console.error('Error:', error);
                    reject(error);
                });

        });
    };

    /**
     * 
     * @param {*} 
     * @returns 
     */
    GameAirdropBoxModelGltfOfSpacecraftController() {
        return new Promise(async (resolve, reject) => {

            axios.get(`${proxy.development.API}/api/v1/temp_api/game/getAllGameCoordinate?sw=GameAirdropBoxModelGltfOfSpacecraft`)
                .then(response => {
                    if (response.data.code === 200) {
                        response.data.message.forEach(item => {
                            const position = Cesium.Cartesian3.fromDegrees(
                                item.coor.lon,
                                item.coor.lat,
                                item.coor.h
                            );
                            const heading = Cesium.Math.toRadians(item.ori.h);
                            const pitch = 0;
                            const roll = 0;
                            const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
                            const orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr);
                            const entity = this._viewer.entities.getById(item.uid);
                            if (entity) this._viewer.entities.removeById(item.uid);
                            this._viewer.entities.add({
                                id: item.uid,
                                name: item.gname,
                                orientation: orientation,
                                position: position,
                                model: {
                                    uri: this.static_http + this.path_models + item.gltf,
                                    clampAnimations: false,
                                    runAnimations: true,
                                    minimumPixelSize: 100,
                                    maximumScale: 50,
                                    show: item.show
                                },
                            });
                        });

                        resolve();
                    };
                })
                .catch(error => {
                    console.error('Error:', error);
                    reject(error);
                });

        });
    };

    /**
     * 
     * @param {*} position 
     * @param {*} time 
     */
    async GameAirdropModelMovePathAnima(time, airTime) {

        axios.get(`${proxy.development.API}/api/v1/temp_api/game/getAllGameCoordinate?sw=GameAirdropModelMovePathAnima`)
            .then(response => {
                if (response.data.code === 200) {
                    response.data.message.forEach(async pos => {
                        const entity = this._viewer.entities.getById(pos.uid);
                        if (!entity) return;

                        this._ui_camera_status.openAirAnima = 1;

                        entity.model.show = true;

                        const property = new Cesium.SampledPositionProperty();
                        entity.position = property;

                        const startTime = Cesium.JulianDate.now();
                        const duration = time;
                        const stopTime = Cesium.JulianDate.addSeconds(startTime, duration, new Cesium.JulianDate());

                        const positionStartPoint = Cesium.Cartesian3.fromDegrees(pos.startLon, pos.startLat, pos.startH);
                        const positionStopPoint = Cesium.Cartesian3.fromDegrees(pos.stopLon, pos.stopLat, pos.stopH);

                        property.addSample(
                            startTime,
                            positionStartPoint
                        );

                        property.addSample(
                            stopTime,
                            positionStopPoint
                        );

                        // this._viewer.clock.startTime = startTime.clone();
                        // this._viewer.clock.stopTime = stopTime.clone();
                        // this._viewer.clock.currentTime = startTime.clone();
                        // this._viewer.clock.clockRange = Cesium.ClockRange.CLAMPED;

                        const heading = 4.65;
                        const pitch = 0;
                        const roll = 0;
                        const orientation = Cesium.Transforms.headingPitchRollQuaternion(
                            positionStartPoint,
                            new Cesium.HeadingPitchRoll(heading, pitch, roll)
                        );

                        entity.orientation = orientation;

                        const checkAnimationComplete = async () => {
                            // console.log(Cesium.JulianDate.compare('AirdropAnima倒计时', this._viewer.clock.currentTime, stopTime));
                            if (Cesium.JulianDate.compare(
                                this._viewer.clock.currentTime,
                                stopTime
                            ) >= airTime) {
                                if (this._OpenAirdropBoxAnima) {
                                    this.GameAirdropBoxModelMovePathAnima(entity, 10);
                                };
                            };

                            if (Cesium.JulianDate.compare(
                                this._viewer.clock.currentTime,
                                stopTime
                            ) >= 0) {
                                console.log('AirdropAnima执行结束');
                                if (this._ui_camera_status.air) this.UiCameraSwitchGameModelMoveAnima('AIR-BOX-MODEL-ksoMY1');

                                this._viewer.clock.onTick.removeEventListener(checkAnimationComplete);

                                entity.position = Cesium.Cartesian3.fromDegrees(pos.stopLon, pos.stopLat, pos.stopH);

                                entity.model.show = false;

                                this._ui_camera_status.air = 0;

                                this._ui_camera_status.openAirAnima = 0;
                            };
                        };

                        this._viewer.clock.onTick.addEventListener(checkAnimationComplete);

                    });
                };
            })
            .catch(error => {
                console.error('Error:', error);
                reject(error);
            });

    };

    /**
     * 
     * @param {*} time 
     */
    async GameAirdropBoxModelMovePathAnima(entity, time, airTime) {
        this._OpenAirdropBoxAnima = false;
        const { longitude, latitude } = await this.Cartesian3TurnCartographic(entity.position.getValue(Cesium.JulianDate.now()));

        axios.get(`${proxy.development.API}/api/v1/temp_api/game/getAllGameCoordinate?sw=GameAirdropBoxModelMovePathAnima`)
            .then(response => {
                if (response.data.code === 200) {
                    response.data.message.forEach(async pos => {
                        const entity = this._viewer.entities.getById(pos.uid);
                        if (!entity) return;

                        entity.model.show = true;

                        const property = new Cesium.SampledPositionProperty();
                        entity.position = property;

                        const startTime = Cesium.JulianDate.now();
                        const duration = time;
                        const stopTime = Cesium.JulianDate.addSeconds(startTime, duration, new Cesium.JulianDate());

                        const positionStartPoint = Cesium.Cartesian3.fromDegrees(longitude, latitude, pos.startH);
                        const positionStopPoint = Cesium.Cartesian3.fromDegrees(pos.stopLon, pos.stopLat, pos.stopH);

                        property.addSample(
                            startTime,
                            positionStartPoint
                        );

                        property.addSample(
                            stopTime,
                            positionStopPoint
                        );

                        // this._viewer.clock.startTime = startTime.clone();
                        // this._viewer.clock.stopTime = stopTime.clone();
                        // this._viewer.clock.currentTime = startTime.clone();
                        // this._viewer.clock.clockRange = Cesium.ClockRange.CLAMPED;

                        const heading = 4.65;
                        const pitch = 0;
                        const roll = 0;
                        const orientation = Cesium.Transforms.headingPitchRollQuaternion(
                            positionStartPoint,
                            new Cesium.HeadingPitchRoll(heading, pitch, roll)
                        );

                        entity.orientation = orientation;

                        const checkAnimationComplete = () => {
                            if (Cesium.JulianDate.compare(
                                this._viewer.clock.currentTime,
                                stopTime
                            ) >= 0) {
                                console.log('AirdropBoxAnima执行结束');
                                this._viewer.clock.onTick.removeEventListener(checkAnimationComplete);

                                entity.position = Cesium.Cartesian3.fromDegrees(pos.stopLon, pos.stopLat, pos.stopH);
                                this._OpenAirdropBoxAnima = true;

                                this._ui_camera_status.box = 0;
                            };
                        };

                        this._viewer.clock.onTick.addEventListener(checkAnimationComplete);
                    });
                };
            })
            .catch(error => {
                console.error('Error:', error);
                reject(error);
            });

    };

    /**
     * 
     * @param {*} imgName 
     * @param {*} isGeoJson 
     * @param {*} jsonName 
     * @returns 
     */
    CreateGeoJsonRivers(img, json) {
        return new Promise(async (resolve, reject) => {
            let position = [];
            const riverGeoJson = await Cesium.GeoJsonDataSource.load(this.static_http + this.path_json + json);
            riverGeoJson.entities.values.forEach(e => {
                position.push(e.polyline.positions.getValue(Cesium.JulianDate.now()));
            });

            const riverPrimitive = new Cesium.GroundPrimitive({
                geometryInstances: new Cesium.GeometryInstance({
                    geometry: new Cesium.PolygonGeometry({
                        polygonHierarchy: new Cesium.PolygonHierarchy(position[0]),
                        // perPositionHeight : true
                    })
                }),
                appearance: new Cesium.EllipsoidSurfaceAppearance({
                    material: new Cesium.Material({
                        fabric: {
                            type: 'Water',
                            uniforms: {
                                normalMap: this.static_http + this.path_img + img,
                                frequency: 100.0,
                                animationSpeed: 0.01,
                                amplitude: 10.0
                            }
                        }
                    })
                })
            });

            this._scene.primitives.add(riverPrimitive);

            resolve();
        });
    };

    /**
     * 
     * @param {*}
     * @returns 
     */
    GameRegionRadiationController() {
        return new Promise((resolve, reject) => {
            axios.get(`${proxy.development.API}/api/v1/temp_api/game/getAllGameCoordinate?sw=GameRegionRadiation`)
                .then(response => {
                    if (response.data.code === 200) {
                        response.data.message.forEach(item => {
                            this._viewer.dataSources.add(
                                Cesium.GeoJsonDataSource.load(this.static_http + this.path_json + item.json, {
                                    stroke: Cesium.Color[item.color],
                                    fill: Cesium.Color[item.fill].withAlpha(0.3),
                                    strokeWidth: 1,
                                    strokeHeigt: 3
                                })
                            );
                        });
                        resolve();
                    };
                })
                .catch(error => {
                    console.error('Error:', error);
                    reject(error);
                });
        });
    };

    /** -------------------------- ^Camera Function ---------------------------- */

    /**
     * 
     * @param {*} flyId 
     */
    async UiCameraSwitchGameModelMoveAnima(flyId) {
        this._viewer.camera.cancelFlight();
        this.GameRegionMapTagIconController(false);

        this.CameraController({
            Ro: true,
            Tr: true,
            Zo: false,
            Ti: true,
            Lo: true
        });
        const model = this._viewer.entities.getById(flyId);
        this._viewer.trackedEntity = model;
        this._viewer.trackedEntity.viewFrom = new Cesium.Cartesian3(-20, -90, 20);
    };

    /**
     * 
     * @param {*} 
     * @function
     * -switch game region map mode
     */
    async UiCameraSwitchGameRegionMap(sw, tagPosition) {
        this._viewer.camera.cancelFlight();
        this._viewer.trackedEntity = undefined;

        this.GameRegionMapLockTagIconController(3);
        this.GameRegionMapTagIconController(true);

        if (sw === 'all') {
            axios.get(`${proxy.development.API}/api/v1/temp_api/game/getAllGameCoordinate?sw=UiCameraSwitchGameRegionMap`)
                .then(response => {
                    if (response.data.code === 200) {

                        this.CameraController({
                            Ro: false,
                            Tr: false,
                            Zo: true,
                            Ti: false,
                            Lo: false
                        });

                        this._viewer.camera.lookAt(
                            Cesium.Cartesian3.fromDegrees(
                                response.data.message.lon,
                                response.data.message.lat,
                                response.data.message.h
                            ),
                            new Cesium.HeadingPitchRange(
                                0,
                                Cesium.Math.toRadians(-90),
                                Cesium.Math.toRadians(90),
                            )
                        );

                    };
                })
                .catch(error => {
                    console.error('Error:', error);
                });
        } else {

            this.CameraController({
                Ro: false,
                Tr: false,
                Zo: true,
                Ti: false,
                Lo: false
            });

            this._viewer.camera.lookAt(
                Cesium.Cartesian3.fromDegrees(
                    tagPosition.longitude,
                    tagPosition.latitude,
                    2500
                ),
                new Cesium.HeadingPitchRange(
                    0,
                    Cesium.Math.toRadians(-90),
                    Cesium.Math.toRadians(90),
                )
            );
        };
    };

    /**
     * 
     * @param {*} m 
     * @function
     * -static modle look at
     */
    async UiCameraSwitchStaticModelLookAt(m) {
        this._viewer.camera.cancelFlight();

        this.CameraController({
            Ro: false,
            Tr: false,
            Zo: false,
            Ti: false,
            Lo: false
        });

        this._viewer.camera.lookAt(
            this._center,
            new Cesium.HeadingPitchRange(
                0,
                Cesium.Math.toRadians(-10),
                this._boundingSphere.radius * m
            )
        );
    };

};


/** ------------------- ^ThreeJS & Enable3d Function ---------------------- */

/**
 * 
 * @param {*} that 
 * @returns 
 * @function
 * -pointer
 */
function getPointer(that) {
    /**@description calculate mouse position in normalized device coordinates */
    // (-1 to +1) for both components
    const pointer = that.input.activePointer;
    const x = (pointer.x / that.cameras.main.width) * 2 - 1;
    const y = -(pointer.y / that.cameras.main.height) * 2 + 1;
    return { x, y };
};

/**
 * @class
 * -create enable3d game scene sky
 */
class CreateEnableGameWorldSceneSkyBox {
    constructor(that) {
        this.http = `${proxy.development.API}`;
        this.static_http = `${proxy.development.STATIC_API}`;
        this.path_img = `${proxy.development.PATH_IMG}`;
        const loader = new THREE.CubeTextureLoader();
        loader.setPath(this.static_http + this.path_img);
        loader.load(["city_sky_px.png", "city_sky_nx.png", "city_sky_py.png", "city_sky_ny.png", "city_sky_pz.png", "city_sky_nz.png"], function (texture) {
            that.third.scene.background = texture;
        });
    };
};

/**
 * @class
 * -enable3d game scene init
 */
class EnableGameWorldSceneInit extends CesiumMain {
    static WoodsScene(callback) {
        delete CreateGsap.instance;
        CreateGsap.init([_cescan, _enCan, 0, 0, 1]);
        callback('EnableAirSceneMain');
        _enCan.style.zIndex = 1;
        _cescan.style.zIndex = -1;
    };
};

/**
 * @class
 * -hdr
 */
class HDR {
    init() {
        const renderer = this.third.renderer;
        renderer.antialias = true;
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.toneMapping = THREE.ACESFilmicToneMapping;
    };
};

/**
 * @class
 * -camera init
 */
class CreateCameraDamping {
    static init(obj) {
        this[obj.name] = obj.orb;
        obj.orb.enableZoom = false;
        obj.orb.enablePan = false;
        obj.orb.enableDamping = true;
        obj.orb.dampingFactor = 0.06;
        obj.orb.maxPolarAngle = Math.PI / 3.2;
        obj.orb.minPolarAngle = Math.PI / 3.2;
    };
};

/**
 * @class
 * -Create All Models Init
 */
class CreateAllModels {
    static async models(item) {
        const building = await this.third.load.gltf(this.static_http + this.path_models + item.glb);
        this.modelsScene.push({
            name: item.glb,
            scene: building.scene
        });
        const glb = new ExtendedObject3D().add(building.scene);
        glb.name = item.glb;
        glb.scale.set(item.sc, item.sc, item.sc);
        glb.position.set(item.pos.x, item.pos.y, item.pos.z);
        this.third.add.existing(glb);
        this.models.push(glb);
        this.modelsExa.push({
            name: item.glb,
            model: glb
        });
    };
};

/**
 * @class
 * -pointer Exit
 */
class PointerExit {
    static init() {
        const raycaster = new THREE.Raycaster();
        this.input.on('pointerdown', () => {
            const { x, y } = getPointer(this);
            raycaster.setFromCamera({ x, y }, this.third.camera);
            const intersection = raycaster.intersectObjects(this.models);
            if (intersection.length > 0) {
                const block = intersection[0].object;
                this.models.forEach(item => item.position.set(0, 30, 0));
                delete CreateGsap.instance;
                CreateGsap.init([_cescan, _enCan, 1, 0, 0]);
                _enCan.style.zIndex = -1;
                _cescan.style.zIndex = 1;

                this.modelCloneScene.forEach((itme, index) => {
                    this.clearCloneModel(itme);

                    if (this.modelCloneScene.length === index + 1) {
                        this.modelCloneScene = [];
                        this.modelCloneExa = [];
                    };
                });
            };
        });
    };
};

/**
 * @class
 * -pointer pick up
 */
class PointerPickUp {
    static init() {
        const raycaster = new THREE.Raycaster();
        this.input.on('pointerdown', () => {
            const { x, y } = getPointer(this);
            raycaster.setFromCamera({ x, y }, this.third.camera);
            const intersection = raycaster.intersectObjects(this.modelCloneExa);
            if (intersection.length > 0) {
                const block = intersection[0].object;

                if (block.parent.name === 'wood.glb') {
                    axios.post(`${proxy.development.API}/api/v1/temp_api/game/playersPickUpMaterials`, {
                        type: 'wood',
                        playerUID: puid
                    }).then(response => {
                        if (response.data.code === 200) {
                            this.clearCloneModel(block);
                        };
                    }).catch(error => {
                        console.error('Error:', error);
                    });
                };

                if (block.parent.name === 'stone.glb') {
                    axios.post(`${proxy.development.API}/api/v1/temp_api/game/playersPickUpMaterials`, {
                        type: 'stone',
                        playerUID: puid
                    }).then(response => {
                        if (response.data.code === 200) {
                            this.clearCloneModel(block);
                        };
                    }).catch(error => {
                        console.error('Error:', error);
                    });
                };
                console.log(block.parent.name)
                if (block.parent.name === 'advocado-half.glb') {
                    axios.post(`${proxy.development.API}/api/v1/temp_api/game/playersPickUpMaterials`, {
                        type: 'advocado',
                        playerUID: puid
                    }).then(response => {
                        if (response.data.code === 200) {
                            this.clearCloneModel(block);
                        };
                    }).catch(error => {
                        console.error('Error:', error);
                    });
                };

                if (block.parent.name === 'plate-dinner.glb') {
                    axios.post(`${proxy.development.API}/api/v1/temp_api/game/playersPickUpMaterials`, {
                        type: 'dinner',
                        playerUID: puid
                    }).then(response => {
                        if (response.data.code === 200) {
                            this.clearCloneModel(block);
                        };
                    }).catch(error => {
                        console.error('Error:', error);
                    });
                };

                if (block.parent.name === 'soda.glb') {
                    axios.post(`${proxy.development.API}/api/v1/temp_api/game/playersPickUpMaterials`, {
                        type: 'soda',
                        playerUID: puid
                    }).then(response => {
                        if (response.data.code === 200) {
                            this.clearCloneModel(block);
                        };
                    }).catch(error => {
                        console.error('Error:', error);
                    });
                };
            };
        });
    };
};

/**
 * @class
 * -enable3d game scene main
 */
class EnableGameWorldSceneMain extends Scene3D {
    constructor() {
        super({
            key: 'EnableAirSceneMain'
        });
        this.http = `${proxy.development.API}`;
        this.static_http = `${proxy.development.STATIC_API}`;
        this.path_models = `${proxy.development.PATH_MODELS}`;

        this.modelCloneScene = [];
        this.modelCloneExa = [];
        this.modelsScene = [];
        this.modelsExa = [];
        this.models = [];
        this.m_name = [];
    };

    async init() {
        this.accessThirdDimension();
    };

    async preload() { };

    async create() {

        const response = await axios.get(`${proxy.development.API}/api/v1/temp_api/game/getEnable3DSceneModels`);
        response.data.code === 200 ? this.m_name = response.data.message.models : null;

        this.sEvents();

        const { orbitControls } = await this.third.warpSpeed('-ground', '-sky');
        CreateCameraDamping.init.call(this, {
            orb: orbitControls,
            name: 'orbitControls'
        });
        new CreateEnableGameWorldSceneSkyBox(this);

        this.sceneDecoration();

        const hdr = new HDR();
        hdr.init.call(this);

        this.m_name.forEach(item => {
            CreateAllModels.models.call(this, item);
        });

        PointerExit.init.call(this);

        PointerPickUp.init.call(this);

    };

    update() {

    };

    sEvents() {
        socket.on('IO_ROOM_ENABLE3D_SWITCH_SCENE_DATA', body => {
            const model = this.modelsExa.find(item => item.name === body.sw);
            model.model.position.set(0, 0, 0);

            switch (body.sw) {
                case 'tree-large.glb':
                    axios.get(`${proxy.development.API}/api/v1/temp_api/game/getRangeRandomDistribution?radius=3`)
                        .then(response => {
                            if (response.data.code === 200) {
                                this.place(response.data.message, 'wood.glb', .5);
                            };
                        }).catch(error => {
                            console.error('Error:', error);
                        });
                    break;
                case 'rock-large.glb':
                    axios.get(`${proxy.development.API}/api/v1/temp_api/game/getRangeRandomDistribution?radius=10`)
                        .then(response => {
                            if (response.data.code === 200) {
                                this.place(response.data.message, 'stone.glb', .2);
                            };
                        }).catch(error => {
                            console.error('Error:', error);
                        });
                    break;
                case 'building-type-p.glb':
                    axios.get(`${proxy.development.API}/api/v1/temp_api/game/getRangeRandomDistribution?radius=10`)
                        .then(response => {
                            if (response.data.code === 200) {
                                const pseudoRandom = ['advocado-half.glb', 'plate-dinner.glb', 'soda.glb'];
                                const food = pseudoRandom[Math.floor(Math.random() * pseudoRandom.length)];
                                this.place(response.data.message, food, 2);
                            };
                        }).catch(error => {
                            console.error('Error:', error);
                        });
                    break;
            };
        });
    };

    place(msg, name, scale) {
        const model = this.modelsScene.find(item => item.name === name);
        msg.coordinate.forEach((item, index) => {
            const scene = model.scene.clone();
            const exa = new ExtendedObject3D().add(scene);
            scene.name = name;
            exa.name = name;
            exa.scale.set(scale, scale, scale)
            exa.position.set(item.x, 0, item.z);
            this.third.scene.add(exa);

            this.modelCloneScene.push(scene);
            this.modelCloneExa.push(exa);

        });
    };

    async sceneDecoration() {
        const ground = await this.third.load.gltf(this.static_http + this.path_models + 'ground.glb');
        const g_glb = new ExtendedObject3D().add(ground.scene);
        g_glb.position.set(6, -0.5, 0);
        this.third.add.existing(g_glb);

        const signpost = await this.third.load.gltf(this.static_http + this.path_models + 'signpost.glb');
        const si_glb = new ExtendedObject3D().add(signpost.scene);
        si_glb.scale.set(6, 6, 6);
        si_glb.position.set(-6, 0, -6);
        this.third.add.existing(si_glb);
    };

    async clearCloneModel(cloneModel) {
        if (!cloneModel) return;

        if (cloneModel.parent) {
            cloneModel.parent.remove(cloneModel);
        };

        if (cloneModel.body) {
            this.third.physics.destroy(cloneModel.body);
        };

        if (cloneModel.anims && cloneModel.anims.mixer) {
            cloneModel.anims.mixer.stopAllAction();
            cloneModel.anims.mixer.uncacheRoot(cloneModel);
            if (cloneModel.anims.mixer._actions) {
                cloneModel.anims.mixer._actions.forEach(action => {
                    cloneModel.anims.mixer.uncacheAction(action);
                });
            };
        };

        cloneModel.traverse(child => {
            if (child.isMesh) {
                if (child.geometry) child.geometry.dispose();
                if (child.material) {
                    if (Array.isArray(child.material)) {
                        child.material.forEach(m => m.dispose());
                    } else {
                        child.material.dispose();
                    };
                };
            };
        });

        axios.get(`${proxy.development.API}/api/v1/temp_api/game/getPlayerIndustryCount?playerUID=${puid}`)
            .then(response => {
                if (response.data.code === 200) {
                    SceneVueInteractiveSubscription.publish('ui_Industry_count', response.data.message);
                };
            }).catch(error => {
                console.error('Error:', error);
            });
    };
};

export {
    CesiumMain,
    EnableGameWorldSceneInit,
    EnableGameWorldSceneMain
};